#include "stdafx.h"
#include "CppUnitTest.h"
#include <vex/iterable/concept/range_concepts.h>
#include <vex/query/from.h>
#include <vex/query/where.h>
#include <vex/iterable/make_any_range.h>
#include <vex.tests/mstest/tests.range/tests.range.h>
#include <vex.tests/mstest/tests.range/range_archetype.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests { namespace query_where_from_stream {
    using namespace boost::assign; 

    TEST_CLASS(test_class) {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.query.where_from_stream")
        END_TEST_CLASS_ATTRIBUTE();

        VEX_TESTS_TRACKED_MEM_CHECK();

        typedef std::stringstream container_t;
        typedef std::istream_iterator<char> input_iterator_t;

        container_t 
            m_container, 
            m_container_empty;

        typedef std::vector<char> reference_container_t;
        
        reference_container_t
            m_reference_container;

        static const char c_separator = ',';
    public:
        void kill_stream()
        {
            container_t()
                .swap(m_container);

            reference_container_t()
                .swap(m_reference_container);
        }

        void restore_stream()
        {
            container_t()
                .swap(m_container);
            reference_container_t()
                .swap(m_reference_container);

            m_container 
                << 1 << c_separator 
                << 2 << c_separator 
                << 3 << c_separator 
                << 4 << c_separator 
                << 5 << c_separator;

            std::copy(input_iterator_t(m_container), input_iterator_t(),
                std::back_inserter(m_reference_container));

            container_t()
                .swap(m_container);

            m_container 
                << 1 << c_separator 
                << 2 << c_separator 
                << 3 << c_separator 
                << 4 << c_separator 
                << 5 << c_separator;
        }

        TEST_METHOD_INITIALIZE(set_up)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_START();
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_FINISH();
        }


        TEST_METHOD(has_expected_traits_and_concept)
        {
            auto r = vex::query::from(input_iterator_t(m_container), input_iterator_t())
                | vex::query::where([](char v) { return std::isdigit(v, std::locale::classic()); });

            //one_pass_range::has_expected_traits<vex::iterable::one_pass, char const&>(r);

            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<decltype(r)>));
        }

        TEST_METHOD(move_front_iteration_succeeds_expected_number_of_times)
        {            
            auto t_predicate = [](char v) { return std::isdigit(v, std::locale::classic()); };

            restore_stream();

            one_pass_range::move_front_iteration_succeeds_expected_number_of_times(
                vex::query::from(input_iterator_t(m_container_empty), input_iterator_t()) 
                | vex::query::where(t_predicate), 
                0);

            restore_stream();

            one_pass_range::move_front_iteration_succeeds_expected_number_of_times(
                vex::query::from(input_iterator_t(m_container), input_iterator_t())
                | vex::query::where(t_predicate),
                5);

            kill_stream();
        }

        TEST_METHOD(move_front_succeeds_n_times_on_range_with_n_elements)
        {
            auto t_predicate = [](char v) { return std::isdigit(v, std::locale::classic()); };

            restore_stream();

            reference_container_t t_exp;
            boost::copy(m_reference_container | boost::adaptors::filtered(t_predicate), 
                std::back_inserter(t_exp));

            restore_stream();

            one_pass_range::move_front_succeeds_n_times_on_range_with_n_elements(
                vex::query::from(input_iterator_t(m_container), input_iterator_t()) 
                | vex::query::where(t_predicate), 
                t_exp.begin(), t_exp.end());

            kill_stream();
        }

        TEST_METHOD(after_calling_move_front_n_times_front_points_to_nth_element)
        {
            auto t_predicate = [](char v) { return std::isdigit(v, std::locale::classic()); };

            restore_stream();

            reference_container_t t_exp_range;
            boost::copy(m_reference_container | boost::adaptors::filtered(t_predicate),
                std::back_inserter(t_exp_range));

            for (auto t_size = t_exp_range.size(); t_size > 0; --t_size) {
                restore_stream();

                one_pass_range::after_calling_move_front_n_times_front_points_to_nth_element(
                    vex::query::from(input_iterator_t(m_container), input_iterator_t()) | vex::query::where(t_predicate), 
                    t_size, 
                    t_exp_range.begin(), t_exp_range.end());
            }

            kill_stream();
        }
    };
}}